

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;



public class Cliente implements Runnable {


    private int tamanho = 2048;
    private int flag = 0;

    public void run() {
        Cliente cliente = new Cliente();
        try {
            try {
                cliente.estabelecerLigacao();
            } catch (InterruptedException ex) {
                Logger.getLogger(Cliente.class.getName()).log(Level.SEVERE, null, ex);
            }
        } catch (IOException ex) {
            Logger.getLogger(Cliente.class.getName()).log(Level.SEVERE, null, ex);
        }
    }


    private float velocidade(int tam,long inicial){
        long actual = System.currentTimeMillis();
        float diferenca = actual-inicial;
        tam=tam/2048;
        float vel = (float) tam/(diferenca*1000);
        return vel;
    }


    public int ligacao(int porta, DatagramSocket datagramCliente, InetAddress iPAddress, int idPacote) throws IOException{

        int ligacao = 1;
        byte[] listaDivisoes;
        int tentativas=0;
        int tipo =0;
        
        while (ligacao != 0 && tentativas < 10) {

            PacoteUDP pacote = new PacoteUDP();
            pacote.setType(2);
            pacote.setID(idPacote);
       
            listaDivisoes = PacoteUDP.divideEmBytes(pacote);
            DatagramPacket datagram = new DatagramPacket(listaDivisoes, listaDivisoes.length, iPAddress, porta);
            
            byte[] receiving = new byte[tamanho];
            DatagramPacket rec = new DatagramPacket(receiving, receiving.length);

            System.out.println("---> a enviar pedido de conexão");
            datagramCliente.send(datagram);
            System.out.println("---> conexão enviada");
            
            try{
                System.out.println("---> à espera de um ack");
                datagramCliente.receive(rec);
                System.out.println("---> ack recebido");
                
                PacoteUDP pacote_ligacao = PacoteUDP.bytesToPacketUDP(rec.getData());
                tipo = pacote_ligacao.getType();          
            }catch(Exception ex) {
                tentativas++;
                Logger.getLogger(Cliente.class.getName()).log(Level.SEVERE, null, ex);
            }
            if (tipo == 2) {
                ligacao = 0;
            }
        }
        return tentativas;
    }

 public int terminarLigacao( int porta, DatagramSocket datagramCliente, InetAddress IPAddress) throws IOException{

        int ligacao = 1;
        byte[] listaDivisoes;
        int tentativas=0;
        int encerra =0;
        
        while (ligacao != 0 && tentativas< 10) {

            PacoteUDP ackligacao = new PacoteUDP();
            ackligacao.setType(2);
            listaDivisoes = PacoteUDP.divideEmBytes(ackligacao);

            DatagramPacket datagram = new DatagramPacket(listaDivisoes, listaDivisoes.length, IPAddress, porta);

            datagramCliente.send(datagram);
 
            byte[] receiving = new byte[tamanho];

            DatagramPacket r1 = new DatagramPacket(receiving, receiving.length);

            try{
                datagramCliente.receive(r1);
                PacoteUDP resligacao = PacoteUDP.bytesToPacketUDP(r1.getData());
                encerra= resligacao.getType();
            }
            catch(Exception ex) {
                tentativas++;
            }
            
            
            if (encerra == 2) {
                ligacao = 0;
                listaDivisoes = null;
                PacoteUDP ackligacao1 = new PacoteUDP();
                ackligacao1.setType(2);
                listaDivisoes = PacoteUDP.divideEmBytes(ackligacao1);

                DatagramPacket datagram2 = new DatagramPacket(listaDivisoes, listaDivisoes.length, IPAddress, porta);

                datagramCliente.send(datagram2);
            }
        }
    return tentativas;
 }



    public void estabelecerLigacao() throws IOException, InterruptedException {

        int i = 0;
        int porta = JDialogConfig.porta;
        byte[] buffer = JDialogEscolherFicheiro.buffer;
        int size = JDialogEscolherFicheiro.size;
        ArrayList<byte[]> listaPacotes = new ArrayList<byte[]>();
        listaPacotes.addAll(JDialogEscolherFicheiro.pacotes);
        PacoteUDP packet;
        byte[] lista;
        int resposta=0;
        int janela = 1;
        long actual;
        int perdas=0;
        int tentativas=0;

        InetAddress iPAddress = InetAddress.getByName(JDialogConfig.ip);
        DatagramSocket datagramClient = new DatagramSocket();
       
        datagramClient.setSoTimeout(3000);
        tentativas=ligacao(porta, datagramClient, iPAddress, listaPacotes.size());

        JDialogEstadoLigacao estadoLigacao = new JDialogEstadoLigacao();
        estadoLigacao.setVisible(true);
      

        if (tentativas<10){ 

            estadoLigacao.setLigado();
            tentativas=0;
            int next = 0;

            while(next < listaPacotes.size() && flag ==0 && tentativas <15){
                actual = System.currentTimeMillis();
                int j = next;
                while(j < (next+janela) && j < listaPacotes.size() && (flag==0)){
                       
                    DatagramPacket datagrama = new DatagramPacket(listaPacotes.get(j), listaPacotes.get(j).length, iPAddress, porta);
                    datagramClient.send(datagrama);
                    System.out.println("--> Cliente a enviar dados para " + datagrama.getAddress().toString() + ':' + datagrama.getPort());
                    flag = estadoLigacao.getFlag();
                    i++;
                    estadoLigacao.setEnviados(i);
                    j++;
                    
                }
                
                int tam=janela*tamanho;
                estadoLigacao.setVelocidade(velocidade(tam,actual));

                byte[] aReceber = new byte[tamanho];
                
                try{
                DatagramPacket r = new DatagramPacket(aReceber, aReceber.length);
                datagramClient.receive(r);
                PacoteUDP p = PacoteUDP.bytesToPacketUDP(r.getData());
                next = p.getID();
                }
                catch(Exception ex){
                    tentativas++; perdas=perdas+janela;
                }
                perdas+= i-next;
                estadoLigacao.setPerdidos(perdas);
                estadoLigacao.setConfirmados(next);
            }
            
            System.out.println("Envio concluído!");
       
            
            PacoteUDP ack = new PacoteUDP();
            ack.setType(2);
            lista = PacoteUDP.divideEmBytes(ack);


            DatagramPacket datagrama = new DatagramPacket(lista,lista.length,iPAddress,porta);
            datagramClient.send(datagrama);

            if(flag ==0){

                tentativas=0;
                tentativas=terminarLigacao(porta, datagramClient, iPAddress);
                if(tentativas <10){
                    datagramClient.close();
                    estadoLigacao.setDesligado();
                    estadoLigacao.setSucesso();
                }
                else{ 
                    datagramClient.close();
                    estadoLigacao.setInSucesso();
                }
            }

            else {
                terminarLigacao(porta, datagramClient, iPAddress);
                datagramClient.close();
                estadoLigacao.setInSucesso();
            }

            if(tentativas ==12){ 
                datagramClient.close();
            }
        }
    }
}
